package com.itheima.stock.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.stock.config.StockInfoConfig;
import com.itheima.stock.entity.*;
import com.itheima.stock.enums.ResponseCode;
import com.itheima.stock.mapper.*;
import com.itheima.stock.properties.MarketProperties;
import com.itheima.stock.response.PageResult;
import com.itheima.stock.response.ResponseResult;
import com.itheima.stock.service.StockService;
import com.itheima.stock.util.DateTimeUtil;
import com.itheima.stock.util.StockCalcUtils;
import com.itheima.stock.vo.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author 28188
 */
@Service
public class StockServiceImpl implements StockService {

    private final StockBusinessMapper stockBusinessMapper;
    private final StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    private final StockMarketLogPriceMapper stockMarketLogPriceMapper;
    private final MarketProperties marketProperties;
    private final StockBlockRtInfoMapper stockBlockRtInfoMapper;
    private final StockRtInfoMapper stockRtInfoMapper;
    private final StockInfoConfig stockInfoConfig;

    public StockServiceImpl(StockBusinessMapper stockBusinessMapper,
                            MarketProperties marketProperties,
                            StockMarketIndexInfoMapper stockMarketIndexInfoMapper,
                            StockMarketLogPriceMapper stockMarketLogPriceMapper,
                            StockBlockRtInfoMapper stockBlockRtInfoMapper,
                            StockRtInfoMapper stockRtInfoMapper,
                            StockInfoConfig stockInfoConfig) {
        this.stockBusinessMapper = stockBusinessMapper;
        this.stockMarketLogPriceMapper = stockMarketLogPriceMapper;
        this.marketProperties = marketProperties;
        this.stockMarketIndexInfoMapper = stockMarketIndexInfoMapper;
        this.stockBlockRtInfoMapper = stockBlockRtInfoMapper;
        this.stockRtInfoMapper = stockRtInfoMapper;
        this.stockInfoConfig = stockInfoConfig;
    }


    /**
     * 获取所有股票经营业务信息列表
     *
     * @return List
     */
    @Override
    public List<StockBusiness> getStockBusiness() {

        return stockBusinessMapper.selectAll();
    }

    /**
     * 获取国内大盘的实时数据
     *
     * @return List
     */
    @Override
    public List<InnerMarketVO> innerIndexAll() {
        // 获取国内大盘id集合
        List<String> ids = marketProperties.getInner();
        // 获取最近的有效的股票交易时间
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        // todo 开发阶段模拟数据
        String mockDate = "20211226105600";
        last = LocalDateTime.parse(mockDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        // 根据国内大盘id集合和日期查询 股票大盘数据详情表
        List<StockMarketIndexInfo> marketIndexInfos = stockMarketIndexInfoMapper.selectByIdsAndDateTime(ids, last);
        // 根据国内大盘id集合和日期查询 股票大盘 开盘价与前收盘价流水
        List<StockMarketLogPrice> marketLogPrices = stockMarketLogPriceMapper.selectByIdsAndDate(ids, last.toLocalDate());
        // 组装数据
        // 将数据库的stockMarketLogPrice表转为 Map
        Map<String, StockMarketLogPrice> priceMap = marketLogPrices.stream()
                .collect(Collectors.toMap(StockMarketLogPrice::getMarketCode, Function.identity()));
        // 将marketIndexInfos和marketLogPrices装入InnerMarketVO
        return marketIndexInfos.stream().map(marketIndexInfo -> {
            StockMarketLogPrice logPrice = priceMap.get(marketIndexInfo.getMarkId());
            return InnerMarketVO.builder()
                    .code(marketIndexInfo.getMarkId())
                    .name(marketIndexInfo.getMarkName())
                    .curDate(marketIndexInfo.getCurTime().toLocalDate().toString())
                    .tradeAmt(marketIndexInfo.getTradeAccount())
                    .tradeVol(marketIndexInfo.getTradeVolume())
                    .tradePrice(marketIndexInfo.getCurrentPrice())
                    .upDown(marketIndexInfo.getUpdownRate())
                    .openPrice(logPrice != null ? logPrice.getOpenPrice() : BigDecimal.ZERO)
                    .preClosePrice(logPrice != null ? logPrice.getPreClosePrice() : BigDecimal.ZERO)
                    .build();
        }).toList();
    }

    /**
     * 获取板块数据
     *
     * @return List
     */
    @Override
    public ResponseResult<List<SectorAllVO>> sectorAllLimit() {
        // 获取最近的有效的股票交易时间
        LocalDateTime last = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        // todo 开发阶段模拟数据
        String mockDate = "20211227105600";
        last = LocalDateTime.parse(mockDate, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        // 获取开盘时间和收盘时间 todo 如果是开市前时间会出错
        LocalDateTime openDate = DateTimeUtil.getOpenDate(last);
        LocalDateTime closeDate = DateTimeUtil.getCloseDate(last);
        // 根据时间获取板块数据
        List<StockBlockRtInfo> infos = stockBlockRtInfoMapper.sectorAllLimit(openDate, closeDate);
        if (CollectionUtils.isEmpty(infos)) {
            return ResponseResult.error(ResponseCode.NO_RESPONSE_DATA);
        }
        // 组装数据
        List<SectorAllVO> sectorAllVOS = infos.stream().map(info -> SectorAllVO.builder()
                .code(info.getLabel())
                .name(info.getBlockName())
                .companyNum(info.getCompanyNum())
                .avgPrice(info.getAvgPrice())
                .updownRate(info.getUpdownRate())
                .tradeAmt(info.getTradeAmount())
                .tradeVol(info.getTradeVolume())
                //todo 时间格式转换
                .curDate(info.getCurTime().toLocalDate().toString())
                .build()).toList();

        return ResponseResult.ok(sectorAllVOS);
    }

    /**
     * 沪深两市个股涨幅分时行情数据查询，以时间顺序和涨幅查询前10条数据
     *
     * @return List
     */
    @Override
    public ResponseResult<List<IncreaseVO>> stockIncreaseLimit() {
        // 获取最近的有效的股票交易时间
        LocalDateTime curDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        // todo 开发阶段模拟数据
        String mockStr = "2021-12-27 09:47:00";
        curDateTime = LocalDateTime.parse(mockStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        List<StockRtInfo> infos = stockRtInfoMapper.stockIncreaseLimit(curDateTime);
        if (CollectionUtils.isEmpty(infos)) {
            return ResponseResult.error(ResponseCode.NO_RESPONSE_DATA);
        }

        List<IncreaseVO> result = infos.stream().map(info -> IncreaseVO.builder()
                        .tradeAmt(info.getTradeAmount())
                        .preClosePrice(info.getPreClosePrice())
                        // 振幅
                        .amplitude(StockCalcUtils.calcAmplitude(info))
                        .code(info.getStockCode())
                        .name(info.getStockName())
                        .curDate(info.getCurTime().toLocalDate().format(DateTimeFormatter.ofPattern("yyyyMMdd")))
                        .tradeVol(info.getTradeVolume())
                        //涨幅
                        .increase(StockCalcUtils.calcIncrease(info))
                        // 涨跌
                        .upDown(StockCalcUtils.calcUpDown(info))
                        .tradePrice(info.getCurPrice())
                        .build()
                ).sorted(Comparator.comparing(
                        IncreaseVO::getIncrease,
                        Comparator.nullsFirst(BigDecimal::compareTo)
                ).reversed())
                .limit(10)
                .toList();

        return ResponseResult.ok(result);
    }

    /**
     * 沪深两市个股涨幅分时行情数据查询，分页查询
     *
     * @param page     页码
     * @param pageSize 每页大小
     * @return List
     */
    @Override
    public PageResult<IncreaseVO> stockPage(Integer page, Integer pageSize) {
        PageHelper.startPage(page, pageSize);
        List<IncreaseVO> infos = stockRtInfoMapper.selectAll();
        return new PageResult<>(new PageInfo<>(infos));
    }

    /**
     * 功能描述：沪深两市涨跌停分时行情数据查询，查询T日每分钟的涨跌停数据（T：当前股票交易日）
     * 查询每分钟的涨停和跌停的数据的同级；
     * 如果不在股票的交易日内，那么就统计最近的股票交易下的数据
     * map:
     * upList:涨停数据统计
     * downList:跌停数据统计
     *
     * @return Map
     */
    @Override
    public UpDownCountVO<OptionVO> upDownCount() {
        //1.获取股票最近的有效交易日期,精确到秒
        LocalDateTime curDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        LocalDateTime openTime = DateTimeUtil.getOpenDate(curDateTime);
        //TODO mock_data 后续数据实时获取时，注释掉
        curDateTime = LocalDateTime.parse("20220106142500", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        openTime = LocalDateTime.parse("20220106092500", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        // todo 转日期数据显示格式
        List<OptionVO> upCount = stockRtInfoMapper.upDownCount(openTime, curDateTime, 1);
        List<OptionVO> downCount = stockRtInfoMapper.upDownCount(openTime, curDateTime, 0);

        return new UpDownCountVO<>(upCount, downCount);
    }

    /**
     * 统计国内A股大盘T日和T-1日成交量对比功能
     *
     * @return List
     */
    @Override
    public TradingVolumeVO<OptionVO> stockTradeVol4InnerMarket() {
        //1.获取最近的股票交易日时间，精确到分钟 T交易日
        LocalDateTime tDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //对应的开盘时间
        LocalDateTime tOpenTime = DateTimeUtil.getOpenDate(tDateTime);

        //TODO 后续注释掉 mock-data
        tDateTime = LocalDateTime.parse("20220103143000", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        tOpenTime = LocalDateTime.parse("20220103093000", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));


        //获取T-1交易日
        LocalDateTime preTTime = DateTimeUtil.getPreviousTradingDay(tDateTime);
        LocalDateTime preTOpenTime = DateTimeUtil.getOpenDate(preTTime);

        //TODO 后续注释掉 mock-data
        preTTime = LocalDateTime.parse("20220102143000", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        preTOpenTime = LocalDateTime.parse("20220102093000", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));


        //2.获取T日的股票大盘交易量统计数据
        List<OptionVO> tData = stockMarketIndexInfoMapper.stockTradeVolCount(marketProperties.getInner(), tOpenTime, tDateTime);

        //3.获取T-1的数据
        List<OptionVO> preTData = stockMarketIndexInfoMapper.stockTradeVolCount(marketProperties.getInner(), preTOpenTime, preTTime);

        //4.组装数据
        TradingVolumeVO<OptionVO> tradingVolumeVO = new TradingVolumeVO<>();
        tradingVolumeVO.setVolList(tData);
        tradingVolumeVO.setYesVolList(preTData);
        return tradingVolumeVO;
    }

    /**
     * 功能描述：统计在当前时间下（精确到分钟），股票在各个涨跌区间的数量
     * 如果当前不在股票有效时间内，则以最近的一个有效股票交易时间作为查询时间点；
     *
     * @return
     */
    @Override
    public UpDownAreaVO stockUpDownScopeCount() {
        //1.获取当前时间下最近的一个股票交易时间 精确到秒
        LocalDateTime avlDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        // TODO 后续删除 mock-data
        avlDateTime = LocalDateTime.parse("20220106095500", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

        // 查询各个涨跌区间的数量
        List<UpDownAreaVO.DomainInfo> domainInfos = stockRtInfoMapper.stockUpDownScopeCount(avlDateTime);

        // 获取预定义的涨跌幅区间集合
        List<String> upDownRange = stockInfoConfig.getUpDownRange();

        // 创建一个Map来存储查询结果，便于查找
        Map<String, UpDownAreaVO.DomainInfo> domainInfoMap = domainInfos.stream()
                .collect(Collectors.toMap(UpDownAreaVO.DomainInfo::getTitle, Function.identity()));

        // 按照预定义顺序排序并补充缺失的区间
        List<UpDownAreaVO.DomainInfo> orderedDomainInfos = upDownRange.stream().map(title -> {
            UpDownAreaVO.DomainInfo domainInfo = domainInfoMap.get(title);
            if (domainInfo == null) {
                // 如果该区间没有数据，则创建一个默认的实体
                domainInfo = new UpDownAreaVO.DomainInfo();
                domainInfo.setTitle(title);
                domainInfo.setCount(0);
            }
            return domainInfo;
        }).collect(Collectors.toList());

        // 封装返回结果
        UpDownAreaVO upDownAreaVO = new UpDownAreaVO();
        upDownAreaVO.setTime(avlDateTime);
        upDownAreaVO.setInfos(orderedDomainInfos);

        return upDownAreaVO;
    }

    @Override
    public List<Stock4MinuteDomain> stockScreenTimeSharing(String code) {
        //1.获取最近有效的股票交易时间
        LocalDateTime curDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //获取当前日期对应的开盘日期
        LocalDateTime openDateTime = DateTimeUtil.getOpenDate(curDateTime);
        //TODO 后续删除 mock-data
        curDateTime = LocalDateTime.parse("20220106142500", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        openDateTime = LocalDateTime.parse("20220106093000", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

        List<Stock4MinuteDomain> maps = stockRtInfoMapper.stockScreenTimeSharing(code, openDateTime, curDateTime);

        return maps;
    }

    @Override
    public ResponseResult<List<Stock4EvrDayDomain>> stockCreenDkLine(String stockCode) {
        //获取当前日期前推20天
        LocalDateTime curDateTime = DateTimeUtil.getLastDateTime4Stock(LocalDateTime.now());
        //前推20
        LocalDateTime pre20Day = curDateTime.minusDays(20);

        //TODO 后续删除
        curDateTime = LocalDateTime.parse("20220114142500", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        pre20Day = LocalDateTime.parse("20211219093000", DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

        List<LocalDateTime> closeDates = stockRtInfoMapper.getCloseDates(stockCode, pre20Day, curDateTime);
        List<Stock4EvrDayDomain> infos = stockRtInfoMapper.getStockCreenDkLineData(stockCode, closeDates);

        return ResponseResult.ok(infos);
    }
}